/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "../asn1/cmscbb_asn1_decode.h"
#include "cmscbb_sdk.h"
#include "cmscbb_x509_def.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../cmscbb_common/cmscbb_def.h"


CVB_STATIC CMSCBB_ERROR_CODE InternalCheckTag(
    const CmscbbAsn1Template* asnTemplate, const CmscbbBerTag* tagReal);

#define CMSCBB_ASN_XKU_CODE_SIGN      0x08   /* same as CMSCBB_PKI_XKU_CODE_SIGN */
#define CMSCBB_ASN_XKU_TIMESTAMP      0x40   /* same as CMSCBB_PKI_XKU_TIMESTAMP */
#define CMSCBB_ASN_XKU_VOUCHERSIGNING 0x200  /* same as CMSCBB_ASN_XKU_VOUCHERSIGNING */
#define CMSCBB_ASN_XKU_RELEASESIGNING 0x400  /* same as CMSCBB_ASN_XKU_RELEASESIGNING */

#define CVB_BUF_REVERT_CURSOR(buf, cur) ((buf)->cursor = (cur))
#define CVB_BUF_GET_CURSOR(buf, cur) ((cur) = (buf)->cursor)
#define CVB_BUF_GET_OFFSET(buf, cur) ((buf)->cursor - (cur))
#define CVB_BUF_GET_POINT(buf) (const CVB_BYTE*)((buf)->val + (buf)->cursor)

#if CMSCBB_NEED_RELOCATE
#if CMSCBB_RELOC_STRICT_CHECK
CVB_VOID RelocateTemplate(CmscbbAsn1Item* asn1Item, CVB_ULONG relocOff)
{
    int iter;

    if (relocOff == 0 || asn1Item == CVB_NULL) {
        return;
    }

    /* Check if the address has been converted */
    if (asn1Item->relocted == CVB_TRUE) {
        return;
    }

    /* Convert analytic function address */
    if (asn1Item->asnFunc != CVB_NULL) {
        asn1Item->asnFunc = (CmscbbAsn1Functions*)((CVB_ULONG)(asn1Item->asnFunc) + relocOff);
        if (asn1Item->asnFunc->decode != CVB_NULL && asn1Item->asnFunc->relocted == CVB_FALSE) {
            asn1Item->asnFunc->decode = (CVB_ASN_DECODE_ITEM)(
                (CVB_ULONG)(asn1Item->asnFunc->decode) + relocOff);
            asn1Item->asnFunc->relocted = CVB_TRUE;
        }
    }

    if (asn1Item->asnTempl != CVB_NULL) {
        /* Convert templ Address */
        asn1Item->asnTempl = (CmscbbAsn1Template*)((CVB_ULONG)(asn1Item->asnTempl) + relocOff);

        /* Recursive call to address translation functions to transform the domain of a child templ */
        for (iter = 0; iter < (int)asn1Item->templCount; ++iter) {
            CmscbbAsn1Template* templ = asn1Item->asnTempl + iter;
            if (templ == CVB_NULL) {
                continue;
            }

            if (templ->asn1Item && (templ->relocted != CVB_NULL) == CVB_FALSE) {
                templ->asn1Item = (CmscbbAsn1Item*)((CVB_ULONG)(templ->asn1Item) + relocOff);
                templ->relocted = CVB_TRUE;
            }
            /*
             * The templ that templ->asn1Item points to is not necessarily redirected even if templ->asn1Item has
             * been redirected
             */
            RelocateTemplate(templ->asn1Item, relocOff);
        }
    }
    asn1Item->relocted = CVB_TRUE;
}
#else
CVB_VOID RelocateTemplate(CmscbbAsn1Item* asn1Item, CVB_ULONG relocOff)
{
    CVB_UINT32 iter;
    CmscbbAsn1Template* templ = CVB_NULL;

    if (relocOff == 0 || asn1Item == CVB_NULL) {
        return;
    }

    /* Check if the address has been converted */
    if ((CVB_ULONG)asn1Item->asnFunc > relocOff) {
        return;
    }

    /* Convert analytic function address */
    if (asn1Item->asnFunc != CVB_NULL) {
        asn1Item->asnFunc = (CmscbbAsn1Functions*)((CVB_ULONG)(asn1Item->asnFunc) + relocOff);
        if (asn1Item->asnFunc->decode != CVB_NULL && (CVB_ULONG)(asn1Item->asnFunc->decode) < relocOff) {
            asn1Item->asnFunc->decode = (CVB_ASN_DECODE_ITEM)((CVB_ULONG)(asn1Item->asnFunc->decode) + relocOff);
        }
    }

    if (asn1Item->asnTempl != CVB_NULL) {
        /* Convert templ Address */
        asn1Item->asnTempl = (CmscbbAsn1Template*)((CVB_ULONG)(asn1Item->asnTempl) + relocOff);

        /* Recursive call to address translation functions to transform the domain of a child templ */
        for (iter = 0; iter < asn1Item->templCount; ++iter) {
            templ = asn1Item->asnTempl + iter;
            if (templ == CVB_NULL) {
                continue;
            }

            if (templ->asn1Item != CVB_NULL && (CVB_ULONG)(templ->asn1Item) < relocOff) {
                templ->asn1Item = (CmscbbAsn1Item*)((CVB_ULONG)(templ->asn1Item) + relocOff);
            }
            /*
             * The templ that templ->asn1Item points to is not necessarily redirected even if template->asn1Item
             * has been redirected
             */
            RelocateTemplate(templ->asn1Item, relocOff);
        }
    }
}
#endif
#endif

/*
 * Prototype    : cmscbb_dec_tag
 * Description  : decode asn1 element tag
 * Params
 *   [IN] b: DER data stream
 *   [OUT] cmscbbBerTag: tag
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:02  tangzonglei  Create
 */
#define CLASS_BIT_POS 6
CVB_STATIC CMSCBB_ERROR_CODE InternalDecTag(CmscbbBuf* b, CmscbbBerTag* cmscbbBerTag)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BYTE tag = 0;

    ret = CmscbbBufGet(b, &tag);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    CMSCBB_MAKE_TAG(*cmscbbBerTag, (tag & CMSCBB_TAG_CLASS_MASK) >> CLASS_BIT_POS,
        (tag & CMSCBB_TAG_PC_MASK) != 0 ? CBF_CONS : CBF_PRIM, tag & CMSCBB_TAG_CODE_MASK);

    /* long form tag, not supported */
    if ((CVB_INT)cmscbbBerTag->code == CMSCBB_TAG_CODE_MASK) {
        CVB_LOG_DEBUG(CMSCBB_ERR_ASN1_TAG_DEC, CVB_NULL);
        return (CMSCBB_ERR_ASN1_TAG_DEC);
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalDecLen
 * Description  : decode asn1 element length
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DER data stream
 *   [OUT] length: length
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:04  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecLen(CmscbbBuf* b, CVB_UINT32* length)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BYTE len = 0;
    CVB_UINT32 typeLen;
    CVB_UINT32 lenTemp = 0;

    ret = CmscbbBufGet(b, &len);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* Check length type */
    typeLen = (CVB_UINT32)(len & CMSCBB_LEN_MASK);
    /* Short length, less than 128 bytes */
    if (typeLen == (CVB_UINT32)len) {
        lenTemp = (CVB_UINT32)typeLen;
    } else if (typeLen == 0 || typeLen > sizeof(CVB_UINT)) { /* typeLen == 0 indefinite length */
        CVB_LOG_WARNING(CMSCBB_ERR_ASN1_LEN_DEC, "indefinit length not supported.");
        return CMSCBB_ERR_ASN1_LEN_DEC;
    } else {
        /* Long length, greater than 128 bytes */
        CVB_UINT i;
        for (i = 0; i < typeLen; ++i) {
            ret = CmscbbBufGet(b, &len);
            if (CVB_FAILED(ret)) {
                CVB_LOG_DEBUG(ret, CVB_NULL);
                return ret;
            }
            lenTemp = (lenTemp << BIT_COUNT_OF_BYTE) + len;
        }
    }

    /* check if length overflow */
    if (lenTemp > (b->bufLen - b->cursor)) {
        CVB_LOG_ERROR(CMSCBB_ERR_ASN1_LEN_DEC, "overflow length.");
        return CMSCBB_ERR_ASN1_LEN_DEC;
    }
    *length = lenTemp;
    return CVB_SUCCESS;
}

/*
 * Prototype    : _cvb_check_tag
 * Description  : Check DER tag
 * Params
 *   [IN] asnTemplate: ASN1 parsing Template
 *   [IN] tagReal: tag
 * Return Value : CVB_SUCCESS if match
 *   Date              Author     Modification
 *   2015/11/11 12:04  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalCheckTag(const CmscbbAsn1Template* asnTemplate, const CmscbbBerTag* tagReal)
{
    CmscbbAsn1Item* asnItem = NULL;
    CmscbbBerTag tagExp = CVB_BER_TAG_INIT;
    CVB_BOOL legalFlag;

    asnItem = asnTemplate->asn1Item;
    if (asnTemplate->tagType == CVB_ASN_IMPLICIT) {
        CMSCBB_MAKE_TAG(tagExp, CBC_CNTX, asnItem->berForm, asnTemplate->tagCode);
    } else {
        CMSCBB_MAKE_TAG(tagExp, asnItem->berClass, asnItem->berForm, asnItem->berCode);
    }

    legalFlag = (tagExp.cls != CBC_ANY_CLASS && tagExp.cls != tagReal->cls) ||
        (tagExp.form != CBF_ANY_FORM && tagExp.form != tagReal->form) ||
        (tagExp.code != CBT_ANY_CODE && tagExp.code != tagReal->code);
    if (legalFlag) {
        CVB_LOG_INFO(CMSCBB_ERR_ASN1_TAG_CHECK, "tag not match");
        return CMSCBB_ERR_ASN1_TAG_CHECK;
    }
    return CVB_SUCCESS;
}

CVB_STATIC CVB_BOOL InternalHasOct(CVB_UINT32 berLen, const CmscbbAsnOcts* asnOcts)
{
    if (berLen == 0 && asnOcts != CVB_NULL) {
        return CVB_TRUE;
    }
    return CVB_FALSE;
}

CVB_STATIC CVB_BOOL InternalIsDerRawTemplFlag(const CmscbbAsn1Template* templ)
{
    if (templ != CVB_NULL && ((CVB_ASN_RAW_DER & templ->flag) != 0)) {
        return CVB_TRUE;
    }
    return CVB_FALSE;
}

/*
 * Prototype    : InternalDecodeOctet
 * Description  : Gets the length and content of the Octet element
 * Params
 *   [IN] b: buffer
 *   [IN] templ:  Templates for parsing
 *   [IN] cur0: buffer cursor
 *   [IN] tag: tag
 *   [IN] asnOcts: asn object
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2016/08/10 11:50  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecodeOctet(CmscbbBuf* b, CVB_UINT32 cur0, const CmscbbAsn1Template* templ,
    CmscbbBerTag tag, CmscbbAsnOcts* asnOcts)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 berLen = 0;

    ret = InternalDecLen(b, &berLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (InternalHasOct(berLen, asnOcts)) {
        /* cp-if the pasnocts has been initialized, you don't have to call it. */
        if (asnOcts != NULL) {
            asnOcts->octs = CVB_NULL;
        }
        return CVB_SUCCESS;
    }

    /*
     * The cvb_asn_raw_der identifier means that the full ASN1 encoding data is saved and the data is deferred for
     * resolution, so tag and length are required
     */
    if (InternalIsDerRawTemplFlag(templ)) {
        CVB_UINT32 tagLen = CVB_BUF_GET_OFFSET(b, cur0);

        berLen += tagLen;
        CVB_BUF_REVERT_CURSOR(b, cur0);
    } else {
        if (tag.code == CBT_INTEGER) {
            /* Remove first sign bit */
            if (berLen > 0 && b->val[b->cursor] == 0) {
                berLen -= 1;
                ++(b->cursor);
            }
        }
    }

    if (asnOcts != NULL) {
        asnOcts->octs = CVB_BUF_GET_POINT(b);
        asnOcts->len = berLen;
        asnOcts->tag.cls = tag.cls;
        asnOcts->tag.code = tag.code;
        asnOcts->tag.form = tag.form;
    }
    ret = CmscbbBufSeek(b, (CVB_INT32)berLen, CBSW_CURRENT);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CVB_BOOL IsIgnoreTempl(const CmscbbAsn1Template *templ)
{
    return (templ != CVB_NULL) && ((CVB_ASN_IGNORE & templ->flag) != 0);
}

/*
 * Prototype    : CmscbbDecodeOctet
 * Description  : decode DER to CmscbbAsnOcts
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN, OUT] v: Memory address to receive cmscbb_asn_octs data
 *   [IN] bytesDecoded: Parse the number of bytes consumed
 *   [IN] templ: Parse the template used
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:04  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeOctet(CmscbbBuf *b, CVB_VOID *v, CVB_UINT32 *bytesDecoded,
    const CmscbbAsn1Template *templ)
{
    CVB_UINT32 cur0;
    CmscbbBerTag tag = CVB_BER_TAG_INIT;
    CmscbbAsnOcts* asnOcts = CVB_NULL;
    CMSCBB_ERROR_CODE ret;

    /* Parameter check */
    if (bytesDecoded == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (IsIgnoreTempl(templ) == CVB_FALSE) {
        if (v == CVB_NULL) {
            CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
            return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
        }
        asnOcts = (CmscbbAsnOcts*)v;
    }

    /* Locate the current data flow read pointer */
    CVB_BUF_GET_CURSOR(b, cur0);

    ret = InternalDecTag(b, &tag);
    CVB_GOTO_ERR_IF_FAIL(ret);

    /*
     * Incoming templates are not empty and will continue to be parsed according to the template, and if empty,
     * the bare data will be saved directly
     */
    if ((templ != CVB_NULL) && (templ->asn1Item != CVB_NULL)) {
        ret = InternalCheckTag(templ, &tag);
        CVB_GOTO_ERR_IF_FAIL(ret);
    }

    ret = InternalDecodeOctet(b, cur0, templ, tag, asnOcts);
    CVB_GOTO_ERR_IF_FAIL(ret);

    /* A fallback read pointer is required if the template's identifier contains Cvb_asn_peek */
    if ((templ != CVB_NULL) && ((CVB_ASN_PEEK & templ->flag) != 0)) {
        CVB_BUF_REVERT_CURSOR(b, cur0);
        *bytesDecoded = 0;
#if CMSCBB_SUPPORT_SIGNED_ATTRIBUTE
        CVB_GOTO_ERR_IF(asnOcts == CVB_NULL, CMSCBB_ERR_ASN1_OCT_PEEK);
        /*
         * The tag type of the cvb_asn_implicit needs to modify its own tag value to the value specified by the child
         * template
         */
        if ((CVB_ASN_IMPLICIT & templ->tagType) != 0) {
            CmscbbAsn1Item* asnRefItem = templ->asn1Item;
            CVB_GOTO_ERR_IF(asnRefItem == CVB_NULL, CMSCBB_ERR_ASN1_OCT_PEEK);

            CMSCBB_MAKE_TAG(asnOcts->tag, asnRefItem->berClass, asnRefItem->berForm, asnRefItem->berCode);
        }
#endif
    } else {
        *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);
    }

    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

/*
 * Prototype    : InternalDecSeqValue
 * Description  : decode asn1 sequence item
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN, OUT] v: Save the memory address of the resolved result
 *   [IN] internalTemplate: Templates for parsing
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalDecSeqValue(CmscbbBuf* b, const CVB_VOID* v,
    const CmscbbAsn1Template* internalTemplate)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 decoded = 0;
    CVB_VOID* fieldValue = CVB_NULL;
    CVB_UINT32 cur0;

    CVB_BUF_GET_CURSOR(b, cur0);

    /* If the tag type is cvb_asn_explicit, you need to peel a layer of tag and length and check the tag value */
    if (internalTemplate->tagType == CVB_ASN_EXPLICIT) {
        CVB_UINT32 elementLen = 0;
        CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
        CmscbbBerTag tagInternal = { CBC_CNTX, CBF_CONS, CBT_EOC };

        tagInternal.code = (CmscbbBerTagCode)internalTemplate->tagCode;

        ret = InternalDecTag(b, &tagReal);
        CVB_GOTO_ERR_IF_FAIL(ret);

        CVB_GOTO_ERR_IF(CmscbbMemCmp(&tagReal, &tagInternal, sizeof(CmscbbBerTag)) != 0, CMSCBB_ERR_ASN1_SEQ_DEC);

        ret = InternalDecLen(b, &elementLen);
        CVB_GOTO_ERR_IF_FAIL(ret);
    }

    CVB_GOTO_ERR_IF(internalTemplate->asn1Item->asnFunc->decode == CVB_NULL, CMSCBB_ERR_ASN1_SEQ_DEC);

    /* Find the memory address of the resolved result by offset */
    fieldValue = (CVB_NULL == v) ? CVB_NULL : (CVB_VOID*)((CVB_PTR)v + internalTemplate->offsetVal);
    ret = internalTemplate->asn1Item->asnFunc->decode(b, fieldValue, &decoded, internalTemplate);
    CVB_GOTO_ERR_IF_FAIL(ret);

    return ret;
CVB_ERR:
    /* Ignore error if flag indicates this field is optional */
    if ((internalTemplate->flag & (CVB_ASN_OPTIONAL | CVB_ASN_DEFAULT)) != 0) {
        CVB_BUF_REVERT_CURSOR(b, cur0);
        ret = CVB_SUCCESS;
    }
    return ret;
}

/*
 * Prototype    : InternalSeqCheckRemainOptional
 * Description  : check if there's any unfinished item, which is not optional
 * Params
 *   [IN] checkTempl: The current template
 *   [IN] remainTempl: Remaining templates
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalSeqCheckRemainOptional(const CmscbbAsn1Template* checkTempl,
    CVB_UINT32 remainTempl)
{
    CVB_UINT32 remainNum = remainTempl;

    while (remainNum > 0) {
        if ((checkTempl->flag & (CVB_ASN_OPTIONAL | CVB_ASN_DEFAULT)) == 0) {
            return CMSCBB_ERR_ASN1_SEQ_DEC;
        }
        ++checkTempl;
        --remainNum;
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalAsn1ParseContent
 * Description  : decode DER tag, length and value
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN] templ: Templates for parsing
 *   [IN] tagForm: Target form Value
 *   [IN] tagCode: Target Code value
 *   [OUT] nElemLen: Returns the length of the ASN1 element
 *   [OUT] tagReal: Returns the TAG of the ASN1 element
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalAsn1ParseContent(CmscbbBuf *b, const CmscbbAsn1Template* templ,
    CmscbbBerForm tagForm, CmscbbBerTagCode tagCode, CVB_UINT32 *elemLen, CmscbbBerTag* tagReal)
{
    CMSCBB_ERROR_CODE ret;

    CmscbbBerTag tagExp = CVB_BER_TAG_INIT;
    if ((templ->tagType & CVB_ASN_IMPLICIT) != 0) {
        CMSCBB_MAKE_TAG(tagExp, CBC_CNTX, tagForm, templ->tagCode);
    } else {
        CMSCBB_MAKE_TAG(tagExp, CBC_UNIV, tagForm, tagCode);
    }

    ret = InternalDecTag(b, tagReal);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* Check that the parsed tag value matches the expected */
    if (CmscbbMemCmp(&tagExp, tagReal, sizeof(CmscbbBerTag)) != 0) {
        return CMSCBB_ERR_ASN1_TAG_DEC;
    }

    ret = InternalDecLen(b, elemLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParmDecSeq(CmscbbBuf* b, CVB_VOID* v, CVB_UINT32* bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CVB_BOOL legalFlag = (b == CVB_NULL) || (v == CVB_NULL) || (bytesDecoded == CVB_NULL) || (templ == CVB_NULL);
    if (legalFlag == CVB_TRUE) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbDecodeSeq
 * Description  : decode DER sequence
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN, OUT] v: Memory address that holds the result of the resolution
 *   [OUT] bytesDecoded: Parse the number of bytes consumed
 *   [IN] templ: Templates for parsing
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeSeq(CmscbbBuf* b, CVB_VOID* v, CVB_UINT32* bytesDecoded, const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 cur0;
    CVB_UINT32 curContent;
    CVB_UINT32 nContentLen = 0;
    CVB_UINT32 nTotalElementLen = 0;
    CmscbbAsn1Template* internalTemplate = CVB_NULL;
    CVB_UINT32 iter;
    CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
    CmscbbAsn1Item* asnItem = templ->asn1Item;

    ret = InternalCheckParmDecSeq(b, v, bytesDecoded, templ);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* Save the initial buffer read pointer */
    CVB_BUF_GET_CURSOR(b, cur0);

    ret = InternalAsn1ParseContent(b, templ, asnItem->berCode == CBT_BITSTRING ? CBF_PRIM : CBF_CONS,
        (CmscbbBerTagCode)asnItem->berCode, &nContentLen, &tagReal);
    CVB_GOTO_ERR_IF_FAIL(ret);

    /* Bit string type skips a unused bits byte */
    if (asnItem->berCode == CBT_BITSTRING) {
        (CVB_VOID)CmscbbBufSeek(b, 1, CBSW_CURRENT);
        --nContentLen;
    }

    CVB_BUF_GET_CURSOR(b, curContent);

    for (internalTemplate = asnItem->asnTempl, iter = 0; iter < asnItem->templCount; ++internalTemplate, ++iter) {
#ifdef CVB_DEBUG
        if (internalTemplate->nameVal) {
            CVB_LOG_DEBUG1(0, "Decode sequence field: %s", internalTemplate->nameVal);
        }
#endif

        /* no element data left, so check the rest of template item should be optional */
        if (nTotalElementLen == nContentLen) {
            ret = InternalSeqCheckRemainOptional(internalTemplate, asnItem->templCount - iter);
            CVB_GOTO_ERR_IF_FAIL(ret);

            break;
        } else {
            CVB_GOTO_ERR_IF(nTotalElementLen >= nContentLen, CMSCBB_ERR_ASN1_SEQ_DEC);
        }

        ret = InternalDecSeqValue(b, ((CVB_ASN_IGNORE & internalTemplate->flag) != 0) ? CVB_NULL : v, internalTemplate);
        CVB_GOTO_ERR_IF_FAIL(ret);

        nTotalElementLen = CVB_BUF_GET_OFFSET(b, curContent);
    }

    /* compare parent length field with total decoded length of all elements */
    CVB_GOTO_ERR_IF(nTotalElementLen != nContentLen, CMSCBB_ERR_ASN1_SEQ_DEC);

    *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);
    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

/*
 * Prototype    : CmscbbDecodeBits
 * Description  : decode DER bits
 * Params
 *   [IN] b: buffer
 *   [IN] v: asn bits
 *   [OUT] bytesDecoded: result of decoded
 *   [IN] templ:  Templates for parsing
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE   Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeBits(CmscbbBuf *b, CVB_VOID *v, CVB_UINT32 *bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
    CVB_UINT32 cur0;
    CVB_UINT32 elemLen = 0;
    CmscbbAsnBits* asnBits = CVB_NULL;
    CVB_BYTE unusedBits = 0;

    if (bytesDecoded == CVB_NULL || templ == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    CVB_BUF_GET_CURSOR(b, cur0);
    ret = InternalAsn1ParseContent(b, templ, CBF_PRIM, CBT_BITSTRING, &elemLen, &tagReal);
    CVB_GOTO_ERR_IF_FAIL(ret);
    /* constructed bit string not supported, although it's legal */
    CVB_GOTO_ERR_IF(elemLen == 0 ||
        tagReal.form == CBF_CONS, CMSCBB_ERR_ASN1_BITS_DEC);
    if ((CVB_ASN_IGNORE & templ->flag) == 0) {
        asnBits = (CmscbbAsnBits*)v;
        CVB_GOTO_ERR_IF(asnBits == CVB_NULL, CMSCBB_ERR_ASN1_BITS_DEC);

        asnBits->tag.cls = tagReal.cls;
        asnBits->tag.code = tagReal.code;
        asnBits->tag.form = tagReal.form;

        /* get unused bits count */
        ret = CmscbbBufGet(b, &unusedBits);
        CVB_GOTO_ERR_IF_FAIL(ret);

        --elemLen;

        asnBits->len = elemLen;

        asnBits->octs = CVB_BUF_GET_POINT(b);
    }

    ret = CmscbbBufSeek(b, (CVB_INT32)elemLen, CBSW_CURRENT);
    CVB_GOTO_ERR_IF_FAIL(ret);

    *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);

    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

/*
 * Prototype    : CmscbbBitsToInt
 * Description  : asn1 bits to integer converter
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN, OUT] v: memory address in which the result is parsed
 *   [IN] bytesDecoded: Resolution of bytes consumed
 *   [IN] templ: Parsing templates
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2016/01/08 14:14  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbBitsToInt(CmscbbBuf *b, CVB_VOID *v, CVB_UINT32 *bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbAsnBits asnBits = {0};
    CmscbbAsnInt* asnInt = (CmscbbAsnInt*)v;

    if (v == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbDecodeBits(b, &asnBits, bytesDecoded, templ);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    if (asnBits.octs == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_ASN1_BITS_DEC, "Decode bits to intger failed.");
        return CMSCBB_ERR_ASN1_BITS_DEC;
    }

    *asnInt = asnBits.octs[0];

    if (asnBits.len == 2) { /* 2 is asn long length count */
        *asnInt = (CmscbbAsnInt)((((CVB_UINT32) * asnInt) << BIT_COUNT_OF_BYTE) | asnBits.octs[1]);
    } else if (asnBits.len > 2) { /* 2 is asn long length count */
        CVB_LOG_ERROR(CMSCBB_ERR_ASN1_BITS_DEC, "unsupported length count");
        return CMSCBB_ERR_ASN1_BITS_DEC;
    }

    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbOidToExku
 * Description  : asn1 oid to extended key usage converter
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN, OUT] v: memory address in which the result is parsed
 *   [OUT] bytesDecoded: Resolution of bytes consumed
 *   [IN] templ: Resolution of bytes consumed
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2016/08/06 16:16  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbOidToExku(CmscbbBuf *b, CVB_VOID *v, CVB_UINT32 *bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbAsnOidBundle extKuSet = {{0}};
    CmscbbAsnInt* asnInt = (CmscbbAsnInt*)v;
    CmscbbAsn1Item* oidItem = CmscbbGetCmscbbAsnOidBundle();
#ifdef CVB_DEBUG
    CmscbbAsn1Template realTempl = { NULL, CMSCBB_RELOC_INIT_VAL 0, CVB_ASN_NO_TAGGING,
        CVB_ASN_NORMAL, 0, CVB_NULL, CVB_NULL };
#else
    CmscbbAsn1Template realTempl = { NULL, CMSCBB_RELOC_INIT_VAL 0, CVB_ASN_NO_TAGGING,
        CVB_ASN_NORMAL, 0, CVB_NULL };
#endif /* CVB_DEBUG */

    (CVB_VOID)templ;
    if (v == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    realTempl.asn1Item = oidItem;
    ret = CmscbbDecodeSetof(b, &extKuSet, bytesDecoded, &realTempl);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    /* extract ext usage */
    if (extKuSet.oidBundle.num > 0) {
        CmscbbAsnOid* pextKuId = &(extKuSet.oidBundle.data[0]);

        CmscbbAoids extKuId = CmscbbFindAoid(pextKuId);
        if (extKuId == AOID_KP_CODESIGNING) {
            *asnInt = (CmscbbAsnInt) (((CVB_UINT32)*asnInt) | CMSCBB_ASN_XKU_CODE_SIGN);
        } else if (extKuId == AOID_KP_TIMESTAMPING) {
            *asnInt = (CmscbbAsnInt)(((CVB_UINT32)*asnInt) | CMSCBB_ASN_XKU_TIMESTAMP);
        } else if (extKuId == AOID_HW_KP_VOUCHERSIGNING) {
            *asnInt = (CmscbbAsnInt)(((CVB_UINT32)*asnInt) | CMSCBB_ASN_XKU_VOUCHERSIGNING);
        } else if (extKuId == AOID_HW_KP_RELEASESIGNING) {
            *asnInt = (CmscbbAsnInt)(((CVB_UINT32)*asnInt) | CMSCBB_ASN_XKU_RELEASESIGNING);
        }
    }

    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParmDecInt(const CVB_UINT32* bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CVB_BOOL legalFlag = (templ == CVB_NULL) || (bytesDecoded == CVB_NULL);
    if (legalFlag) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbDecodeInt
 * Description  : decode asn1 element to integer
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN] v: memory address in which the result is parsed
 *   [IN] bytesDecoded: Resolution of bytes consumed
 *   [IN] templ: Resolution of bytes consumed
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/11 12:06  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeInt(CmscbbBuf* b, CVB_VOID* v, CVB_UINT32* bytesDecoded, const CmscbbAsn1Template* templ)
{
    CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
    CVB_UINT32 elemLen = 0;
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 cur0 = 0;
    CmscbbBerTagCode tagCode = CBT_NULL_CODE;

    ret = InternalCheckParmDecInt(bytesDecoded, templ);
    CVB_GOTO_ERR_IF(CVB_FAILED(ret), ret);

    CVB_BUF_GET_CURSOR(b, cur0);

    if (templ->asn1Item != CVB_NULL) {
        tagCode = templ->asn1Item->berCode;
    }

    ret = InternalAsn1ParseContent(b, templ, CBF_PRIM, tagCode, &elemLen, &tagReal);
    CVB_GOTO_ERR_IF_FAIL(ret);

    if ((CVB_ASN_IGNORE & templ->flag) == 0) {
        CVB_ULONG value;
        CVB_BYTE byte = 0;
        CmscbbAsnInt* asnInt = (CmscbbAsnInt*)v;
        CVB_INT i;

        CVB_GOTO_ERR_IF(asnInt == CVB_NULL, CMSCBB_ERR_ASN1_INT_DEC);

        /* the int data should less then 4 byte */
        CVB_GOTO_ERR_IF(elemLen > sizeof(CmscbbAsnInt), CMSCBB_ERR_ASN1_INT_DEC);

        ret = CmscbbBufGet(b, &byte);
        CVB_GOTO_ERR_IF(CVB_FAILED(ret), CMSCBB_ERR_ASN1_INT_DEC);

        if (tagCode == CBT_BOOLEAN) {
            CVB_GOTO_ERR_IF(elemLen != 1, CMSCBB_ERR_ASN1_BOOL_DEC);
            value = (CVB_ULONG)(byte == 0xFF); /* 0xFF is mark bit */
        } else {
            if ((byte & 0x80) != 0) { /* 0x80 is top bit of first byte is sign bit */
                value = ((CVB_ULONG) - 1 ^ 0xFF) | byte; /* 0xFF is mark bit */
            } else {
                value = byte;
            }
        }

        for (i = 1; i < (CVB_INT)elemLen; ++i) {
            ret = CmscbbBufGet(b, &byte);
            CVB_GOTO_ERR_IF(CVB_FAILED(ret), CMSCBB_ERR_ASN1_INT_DEC);

            value = (value << BIT_COUNT_OF_BYTE) | (CVB_ULONG)byte;
        }

        *asnInt = (CmscbbAsnInt)value;
    } else {
        (CVB_VOID)CmscbbBufSeek(b, (CVB_INT32)elemLen, CBSW_CURRENT);
    }

    *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);

    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

/*
 * Prototype    : CmscbbDecodeSetof
 * Description  : decode DER set/sequence of
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN] v: memory address in which the result is parsed
 *   [IN] bytesDecoded: Resolution of bytes consumed
 *   [IN] templ: Resolution of bytes consumed
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 * Remarks      : The template for "sequence of"/"set of" is very special,
 *   it should only have one item.
 *   Date              Author     Modification
 *   2015/11/11 12:07  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeSetof(CmscbbBuf* b, CVB_VOID* v, CVB_UINT32* bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 cur0;
    CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
    CVB_UINT32 nContentLen = 0;
    CVB_UINT32 nTotalElemLen = 0;
    CVB_UINT32 nElemLen = 0;
    CmscbbAsn1Template* internalTempl = CVB_NULL;
    CmscbbListDummy* list = CVB_NULL;
    CVB_UINT32 itemCount = 0;
    CVB_UINT32 maxItem;
    CVB_PTR data = (CVB_PTR)v;

    if ((v == CVB_NULL) || (bytesDecoded == CVB_NULL) || (templ == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    /* the template's item should be the one! the template is special for "sequence of" */
#define ITEM_COUNT_OF_TEMPLATE_OF_SETOF 1
    if (templ->asn1Item->templCount != ITEM_COUNT_OF_TEMPLATE_OF_SETOF) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    CVB_BUF_GET_CURSOR(b, cur0);

    ret = InternalAsn1ParseContent(b, templ, CBF_CONS, (CmscbbBerTagCode)templ->asn1Item->berCode,
        &nContentLen, &tagReal);
    CVB_GOTO_ERR_IF(CVB_FAILED(ret), CMSCBB_ERR_ASN1_SETOF_DEC);

    internalTempl = templ->asn1Item->asnTempl;
    /* Gets the memory address of the array by offset */
    list = (CmscbbListDummy*)(data + internalTempl->offsetVal);
    /* Gets the maximum number of arrays */
    maxItem = (CVB_UINT32)(CVB_PTR)internalTempl->defaultVal;

    CVB_GOTO_ERR_IF(internalTempl->asn1Item->asnFunc->decode == CVB_NULL, CMSCBB_ERR_ASN1_SETOF_DEC);

    while (nContentLen > nTotalElemLen) {
        CVB_VOID* itemPtr = CVB_NULL;
        CVB_GOTO_ERR_IF(itemCount >= maxItem, CMSCBB_ERR_ASN1_SETOF_DEC);

        /* The memory address where the result is stored */
        itemPtr = (CVB_VOID*)((CVB_PTR)list->data + internalTempl->asn1Item->itemSzie * itemCount);
        ret = internalTempl->asn1Item->asnFunc->decode(b, itemPtr, &nElemLen, internalTempl);
        CVB_GOTO_ERR_IF(CVB_FAILED(ret), CMSCBB_ERR_ASN1_SETOF_DEC);

        ++itemCount;
        nTotalElemLen += nElemLen;
    }

    CVB_GOTO_ERR_IF(nContentLen != nTotalElemLen, CMSCBB_ERR_ASN1_SETOF_DEC);
    list->num = itemCount;

    *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);

    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

CVB_STATIC CVB_VOID InternalDecodeMatemplateInit(CmscbbAsn1Template* itemTempl)
{
    itemTempl->asn1Item = CmscbbGetCmscbbX509ExtensionEntry();
#if CMSCBB_RELOC_STRICT_CHECK
    itemTempl->relocted = CVB_FALSE;
#endif
    itemTempl->tagCode = 0;
    itemTempl->tagType = CVB_ASN_NO_TAGGING;
    itemTempl->flag = CVB_ASN_NORMAL;
    itemTempl->offsetVal = 0;
#ifdef CVB_DEBUG
    itemTempl->nameVal = CVB_NULL;
#endif
    itemTempl->defaultVal = CVB_NULL;
}

/*
 * Prototype    : CmscbbDecodeMap
 * Description  : Resolves the ASN1 element to the type specified in the map
 * Params
 *   [IN] pVrf: Validation context
 *   [IN] b: DERData flow
 *   [IN] v: memory address in which the result is parsed
 *   [IN] bytesDecoded: Resolution of bytes consumed
 *   [IN] templ: Resolution of bytes consumed
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2016/08/06 16:46  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbDecodeMap(CmscbbBuf* b, CVB_VOID* v, CVB_UINT32* bytesDecoded,
    const CmscbbAsn1Template* templ)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 cur0;
    CmscbbBerTag tagReal = CVB_BER_TAG_INIT;
    CVB_UINT32 nConLen = 0;
    CVB_UINT32 nTotalElemLen = 0;
    CmscbbX509ExtensionEntry extItem = {{0}, 0, {0}};

    if (v == CVB_NULL || bytesDecoded == CVB_NULL || templ == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    CVB_BUF_GET_CURSOR(b, cur0);

    ret = InternalAsn1ParseContent(b, templ, CBF_CONS, (CmscbbBerTagCode)templ->asn1Item->berCode, &nConLen, &tagReal);
    CVB_GOTO_ERR_IF(CVB_FAILED(ret), CMSCBB_ERR_ASN1_SETOF_DEC);

    while (nConLen > nTotalElemLen) {
        CVB_UINT32 nDecoded = 0;
        CmscbbAsn1Item* asnItem = templ->asn1Item;
        CmscbbAsn1Template* internalTempl = asnItem->asnTempl;
        CmscbbAoids aoid;
        CVB_INT iter = 0;
        CmscbbAsn1Template itemTempl;

        /* in case of lack of base address */
        InternalDecodeMatemplateInit(&itemTempl);

        /* Read a Cmscbb_x509_extension_entry type object from the buffer first */
        ret = CmscbbDecodeSeq(b, &extItem, &nDecoded, &itemTempl);
        CVB_GOTO_ERR_IF_FAIL(ret);
        CVB_GOTO_ERR_IF(CVB_NULL == extItem.extnValue.octs || extItem.extnValue.len == 0, CMSCBB_ERR_ASN1_SETOF_DEC);

        nTotalElemLen += nDecoded;

        /* Traversing the map to find the real template */
        aoid = CmscbbFindAoid(&extItem.extnId);
        for (; iter < (CVB_INT)asnItem->templCount; ++iter, ++internalTempl) {
            if (internalTempl->tagCode == aoid) {
                CmscbbBuf bufVal = {0};
                bufVal.val = extItem.extnValue.octs;
                bufVal.bufLen = extItem.extnValue.len;

                CVB_GOTO_ERR_IF(internalTempl->asn1Item->asnFunc->decode == CVB_NULL, CMSCBB_ERR_ASN1_SETOF_DEC);
                ret = internalTempl->asn1Item->asnFunc->decode(&bufVal,
                    (CVB_VOID*)((CVB_PTR)v + internalTempl->offsetVal), &nDecoded, internalTempl);
                CVB_GOTO_ERR_IF_FAIL(ret);
                break;
            }
        }
    }

    CVB_GOTO_ERR_IF(nConLen != nTotalElemLen, CMSCBB_ERR_ASN1_SETOF_DEC);

    *bytesDecoded = CVB_BUF_GET_OFFSET(b, cur0);

    return ret;
CVB_ERR:
    CVB_BUF_REVERT_CURSOR(b, cur0);
    return ret;
}

CMSCBB_ERROR_CODE CmscbbAsnDecode(const CVB_BYTE* buf, CVB_UINT32 bufLen, CmscbbAsn1Item* asnItem, CVB_BYTE flag,
    CVB_VOID** result, CVB_UINT32* decodedLen)
{
    CVB_VOID* res = CVB_NULL;
    CMSCBB_ERROR_CODE ret;
    CmscbbBuf cmscbbBuf = {0};

    CmscbbAsn1Template templ;
    templ.asn1Item = asnItem;
#if CMSCBB_RELOC_STRICT_CHECK
    templ.relocted = CVB_FALSE;
#endif
    templ.tagCode = 0;
    templ.tagType = CVB_ASN_NO_TAGGING;
    templ.flag = (CVB_UINT16)flag;
    templ.offsetVal = 0;
#ifdef CVB_DEBUG
    templ.nameVal = CVB_NULL;
#endif
    templ.defaultVal = CVB_NULL;

    if ((buf == CVB_NULL) || (bufLen == 0) ||
        (asnItem == CVB_NULL) || (result == CVB_NULL) || (decodedLen == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    CVB_GOTO_ERR_IF(asnItem->asnFunc->decode == CVB_NULL, CMSCBB_ERR_ASN1_BASE);

    ret = CmscbbBufInit(&cmscbbBuf, buf, bufLen);
    CVB_GOTO_ERR_IF_FAIL(ret);

    ret = CmscbbMallocWith0(&res, asnItem->itemSzie);
    CVB_GOTO_ERR_IF_FAIL(ret);

    ret = asnItem->asnFunc->decode(&cmscbbBuf, res, decodedLen, &templ);
    CVB_GOTO_ERR_IF_FAIL(ret);

    *result = res;
    return ret;
CVB_ERR:
    CmscbbFreeWithNullCheck(res);
    return ret;
}
